home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2000 #1 / Amiga Plus CD - 2000 - No. 1.iso / Tools / Text / Edit / GoldED-Demo / installdata / golded / developer / api / examples / info / funcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-12-03  |  30.6 KB  |  1,271 lines

  1. /* -----------------------------------------------------------------------------
  2.  
  3.  info.api ©1999 Dietmar Eilert
  4.  
  5.  Shows information for word under cursor. This client requires a dictionary
  6.  file which lists the known words and the associated help texts. You can use
  7.  the file of the sas-c add-on, ie. "golded:add-ons/sas-c/quickinfo/os3.words".
  8.  The user must specify the name of the dictionary file as startup option of
  9.  this plug-in. Startup options can be specified in the API dialog of GoldED:
  10.  doubleclick on a plug-in name to set its command string.
  11.  
  12.  Dice:
  13.  
  14.  DMAKE
  15.  
  16.  -------------------------------------------------------------------------------
  17.  
  18. */
  19.  
  20. #include "defs.h"
  21.  
  22. /// "prototypes"
  23.  
  24. // library functions
  25.  
  26. Prototype LibCall struct APIClient *APIMountClient     (__A0 struct APIMessage *, __A1 char *);
  27. Prototype LibCall void              APICloseClient     (__A0 struct APIClient  *, __A1 struct APIMessage *);
  28. Prototype LibCall void              APIBriefClient     (__A0 struct APIClient  *, __A1 struct APIMessage *);
  29. Prototype LibCall void              APIFree            (__A0 struct APIClient  *, __A1 struct APIOrder   *);
  30.  
  31. // private functions
  32.  
  33. Prototype UBYTE                    *CheckThisWord      (struct PlugInContext *, UBYTE *, UWORD, UWORD);
  34. Prototype void                      ClearContainer     (struct APIMessage *, struct PlugInContext *);
  35. Prototype UWORD                     ComputeX           (struct TextFont *, UWORD);
  36. Prototype UWORD                     ComputeY           (struct TextFont *, UWORD);
  37. Prototype ULONG                     DispatchContainer  (struct PlugInContext *, struct APIMessage *);
  38. Prototype ULONG                     DispatchKey        (struct PlugInContext *, struct APIMessage *);
  39. Prototype struct FileInfoBlock     *FileInfo           (UBYTE *, struct FileInfoBlock *);
  40. Prototype void                      GetVScreenSize     (struct Screen *, UWORD *, UWORD *);
  41. Prototype UWORD                     Hashcode           (UWORD, UWORD, UWORD);
  42. Prototype struct Dictionary        *LoadDictionary     (UBYTE *, BOOL);
  43. Prototype struct Window            *Message            (UBYTE *, BOOL, struct Window *);
  44. Prototype struct Window            *OpenMessageWin     (UBYTE *, BOOL);
  45. Prototype UBYTE                    *ReadFile           (UBYTE *, ULONG *);
  46. Prototype BOOL                      ResolveSymbolicPath(UBYTE *);
  47. Prototype void                      ShowInfo           (struct APIMessage *, struct PlugInContext *, UBYTE *);
  48. Prototype WORD                      memicmp            (UBYTE *, UBYTE *, UWORD);
  49.  
  50. ///
  51. /// "globals"
  52.  
  53. struct SignalSemaphore QuickInfoSemaphore;           // used to serialize functions
  54. struct List            DictionaryList;               // root of dictionaries
  55.  
  56. // arrays
  57.  
  58. BOOL IsSPC     [256];
  59. BOOL IsBOUNDARY[256];
  60.  
  61. ///
  62. /// "library functions"
  63.  
  64. LibCall struct APIClient *
  65. APIMountClient(__A0 struct APIMessage *apiMsg, __A1 char *args)
  66. {
  67.     struct PlugInContext *context = NULL;
  68.  
  69.     // STATUS/S,IGNORECASE/S,FILE/A
  70.  
  71.     if (args && *args) {
  72.  
  73.         static struct TagItem tags[] = { TAG_DONE };
  74.  
  75.         struct RDArgs *rdArgs;
  76.  
  77.         if (rdArgs = (struct RDArgs *)AllocDosObject(DOS_RDARGS, NULL)) {
  78.  
  79.             LONG argArray[] = { 0, 0, 0 };
  80.  
  81.             UBYTE buffer[MAX_PATHLEN];
  82.  
  83.             // LF-terminate arguments
  84.  
  85.             strcpy(buffer, args);
  86.  
  87.             strcat(buffer, "\12");
  88.  
  89.             // parse arguments (STATUS/S, FILE/K)
  90.  
  91.             rdArgs->RDA_Source.CS_Buffer = buffer;
  92.             rdArgs->RDA_Source.CS_Length = strlen(buffer);
  93.             rdArgs->RDA_Source.CS_CurChr = 0;
  94.             rdArgs->RDA_DAList           = 0;
  95.             rdArgs->RDA_Buffer           = NULL;
  96.  
  97.             if (ReadArgs("STATUS/S,IGNORECASE/S,FILE/A", argArray, rdArgs)) {
  98.  
  99.                 struct Dictionary *dictionary;
  100.  
  101.                 if (dictionary = LoadDictionary((UBYTE *)argArray[2], (BOOL)argArray[1])) {
  102.  
  103.                     // allocate local data for this plug-in instance (extended APIClient structure)
  104.  
  105.                     if (context = (struct PlugInContext *)AllocVec(sizeof(struct PlugInContext), MEMF_PUBLIC | MEMF_CLEAR)) {
  106.  
  107.                         context->Dictionary = dictionary;
  108.                         context->Status     = (BOOL)argArray[0];
  109.                         context->Ignorecase = (BOOL)argArray[1];
  110.  
  111.                         context->APIClient.api_APIVersion = API_INTERFACE_VERSION;
  112.                         context->APIClient.api_Version    = 4;
  113.                         context->APIClient.api_Name       = "Info";
  114.                         context->APIClient.api_Info       = "Show help text for word under cursor.";
  115.                         context->APIClient.api_Commands   = NULL;
  116.                         context->APIClient.api_Serial     = 0;
  117.                         context->APIClient.api_Classes    = API_CLASS_SYSTEM | API_CLASS_KEY;
  118.  
  119.                         // show messages in status line or in a container ?
  120.  
  121.                         if (context->Status) {
  122.  
  123.                             context->APIClient.api_Area = NULL;
  124.                         }
  125.                         else {
  126.  
  127.                             // prepare the container request
  128.  
  129.                             context->APIArea.api_Width      = 20;
  130.                             context->APIArea.api_Height     = 2;
  131.                             context->APIArea.api_UnitsX     = API_UNITS_FONT;
  132.                             context->APIArea.api_UnitsY     = API_UNITS_FONT;
  133.                             context->APIArea.api_Alignment  = API_ALIGN_BOTTOM;
  134.                             context->APIArea.api_Style      = API_STYLE_STANDARD;
  135.                             context->APIArea.api_IDCMP      = 0;
  136.  
  137.                             context->APIClient.api_Classes |= API_CLASS_CONTAINER;
  138.                             context->APIClient.api_Area     = &context->APIArea;
  139.                         }
  140.                     }
  141.                 }
  142.  
  143.                 FreeArgs(rdArgs);
  144.             }
  145.             else {
  146.  
  147.                 if (Fault(IoErr(), "ERROR", buffer, sizeof(buffer)))
  148.  
  149.                     Message(buffer, TRUE, NULL);
  150.             }
  151.  
  152.             FreeDosObject(DOS_RDARGS, rdArgs);
  153.         }
  154.     }
  155.     else
  156.         Message("No dictionary file specified !", TRUE, NULL);
  157.  
  158.     return((struct APIClient *)context);
  159. }
  160.  
  161. LibCall void
  162. APICloseClient(__A0 struct APIClient *handle, __A1 struct APIMessage *apiMsg)
  163. {
  164.     FreeVec(handle);
  165. }
  166.  
  167. LibCall void
  168. APIBriefClient(__A0 struct APIClient *handle, __A1 struct APIMessage *apiMsg)
  169. {
  170.     struct APIMessage *msg;
  171.  
  172.     // handle notify sent by host program
  173.  
  174.     for (msg = apiMsg; msg; msg = msg->api_Next) {
  175.  
  176.         if (msg->api_State == API_STATE_NOTIFY) {
  177.  
  178.             switch (msg->api_Class) {
  179.  
  180.                 case API_CLASS_KEY:
  181.  
  182.                     msg->api_Error = DispatchKey((struct PlugInContext *)handle, msg);
  183.  
  184.                     break;
  185.  
  186.                 case API_CLASS_SYSTEM:
  187.  
  188.                     break;
  189.  
  190.                 case API_CLASS_CONTAINER:
  191.  
  192.                     msg->api_Error = DispatchContainer((struct PlugInContext *)handle, msg);
  193.  
  194.                     break;
  195.  
  196.                 default:
  197.  
  198.                     msg->api_Error = API_ERROR_UNKNOWN;
  199.             }
  200.         }
  201.     }
  202. }
  203.  
  204. LibCall void
  205. APIFree(__A0 struct APIClient *handle, __A1 struct APIOrder *apiOrder)
  206. {
  207.     // no ressources to be freed
  208. }
  209.  
  210. ///
  211. /// "private functions"
  212.  
  213. /* ------------------------------- LoadDictionary ------------------------------
  214.  
  215.  Load dictionary if not yet loaded. We simply load the dictionary into a memory
  216.  block, locate the strings in the dictionary (templates and associated help
  217.  texts), NULL-terminate them and build a list of dictionary entries where we
  218.  store pointers to the strings. We also build a very simple hash table where we
  219.  remember the first template beginning with a given character.
  220.  
  221.  Dictionary format (ASCII file, templates MUST BE SORTED PROPERLY):
  222.  
  223.  <template> <help text>
  224.  <template> <help text>
  225.  ...
  226.  
  227. */
  228.  
  229. struct Dictionary *
  230. LoadDictionary(file, ignorecase)
  231.  
  232. UBYTE *file;
  233. BOOL   ignorecase;
  234. {
  235.     UBYTE              path[MAX_PATHLEN];
  236.     struct Dictionary *dictionary;
  237.  
  238.     // serialize access
  239.  
  240.     ObtainSemaphore(&QuickInfoSemaphore);
  241.  
  242.     strcpy(path, file);
  243.  
  244.     if (ResolveSymbolicPath(path)) {
  245.  
  246.         // search list of resident dictionaries
  247.  
  248.         dictionary = (struct Dictionary *)FindName(&DictionaryList, path);
  249.  
  250.         // dictionary not yet loaded ?
  251.  
  252.         if (dictionary == NULL) {
  253.  
  254.             UBYTE *buffer;
  255.             ULONG  size;
  256.  
  257.             if (buffer = ReadFile(path, &size)) {
  258.  
  259.                 if (dictionary = (struct Dictionary *)AllocVec(sizeof(struct Dictionary), MEMF_CLEAR | MEMF_PUBLIC)) {
  260.  
  261.                     UBYTE *start;
  262.                     UBYTE *next;
  263.                     UBYTE *last;
  264.  
  265.                     strcpy(dictionary->File, path);
  266.  
  267.                     dictionary->Data         = buffer;
  268.                     dictionary->Node.ln_Name = dictionary->File;
  269.  
  270.                     NewList(&dictionary->Entries);
  271.  
  272.                     // process dictionary
  273.  
  274.                     start = buffer;
  275.                     last  = buffer + size;
  276.  
  277.                     for (next = buffer; next < last; ++next) {
  278.  
  279.                         // end of entry (example: "xxxx yyyy") detected ?
  280.  
  281.                         if (*next == 10) {
  282.  
  283.                             // minimum entry "? ?" + <LF>
  284.  
  285.                             if (next != start) {
  286.  
  287.                                 UBYTE *quickinfo;
  288.  
  289.                                 // NULL-terminate help text
  290.  
  291.                                 *next = 0;
  292.  
  293.                                 // find end of template and start of help text
  294.  
  295.                                 for (quickinfo = start; *quickinfo; ++quickinfo) {
  296.  
  297.                                     // end of template ?
  298.  
  299.                                     if (*quickinfo == ' ') {
  300.  
  301.                                         struct Template *template;
  302.  
  303.                                         // NULL-terminate template
  304.  
  305.                                         *quickinfo++ = 0;
  306.  
  307.                                         if (template = (struct Template *)AllocVec(sizeof(struct Template), MEMF_CLEAR | MEMF_PUBLIC)) {
  308.  
  309.                                             UWORD hashcode = Hashcode(start[0], start[1], quickinfo - start - 1);
  310.  
  311.                                             template->Info    = quickinfo;
  312.                                             template->Len     = quickinfo - start - 1;
  313.                                             template->Keyword = start;
  314.  
  315.                                             AddTail(&dictionary->Entries, &template->Node);
  316.  
  317.                                             if (dictionary->Hash[hashcode] == NULL)
  318.  
  319.                                                 dictionary->Hash[hashcode] = template;
  320.                                         }
  321.  
  322.                                         break;
  323.                                     }
  324.                                     else if (ignorecase) {
  325.  
  326.                                         // case-insensitive dictionary
  327.  
  328.                                         *quickinfo = toupper(*quickinfo);
  329.                                     }
  330.                                 }
  331.                             }
  332.  
  333.                             start = next + 1;
  334.                         }
  335.                     }
  336.  
  337.                     AddTail(&DictionaryList, &dictionary->Node);
  338.                 }
  339.                 else {
  340.  
  341.                     Message("Out of memory !", TRUE, NULL);
  342.  
  343.                     FreeVec(buffer);
  344.                 }
  345.             }
  346.             else
  347.                 Message("Couldn't load dictionary file", TRUE, NULL);
  348.         }
  349.     }
  350.     else {
  351.  
  352.         Message("Dictionary file not found", TRUE, NULL);
  353.  
  354.         dictionary = NULL;
  355.     }
  356.  
  357.     ReleaseSemaphore(&QuickInfoSemaphore);
  358.  
  359.     return(dictionary);
  360. }
  361.  
  362.  
  363. /* --------------------------------- DispatchKey -------------------------------
  364.  
  365.  Dispatch incoming API event
  366.  
  367. */
  368.  
  369. ULONG
  370. DispatchKey(context, apiMsg)
  371.  
  372. struct PlugInContext *context;
  373. struct APIMessage    *apiMsg;
  374. {
  375.     ULONG retval = API_ERROR_OK;
  376.  
  377.     switch (apiMsg->api_Action) {
  378.  
  379.         case API_ACTION_RAWKEY:
  380.         case API_ACTION_VANILLAKEY:
  381.  
  382.             {
  383.                 struct EditConfig *config;
  384.                 UBYTE             *info;
  385.  
  386.                 config = (struct EditConfig *)apiMsg->api_Instance->api_Environment;
  387.  
  388.                 // check word under cursor
  389.  
  390.                 if (info = CheckThisWord(context, config->CurrentBuffer, config->CurrentLen, config->Column))
  391.  
  392.                     ShowInfo(apiMsg, context, info);
  393.             }
  394.  
  395.             break;
  396.  
  397.         default:
  398.  
  399.             retval = API_ERROR_UNKNOWN;
  400.     }
  401.  
  402.     return(retval);
  403. }
  404.  
  405.  
  406. /* -------------------------------- FileInfo -------------------------------------
  407.  
  408.  Get a file's info block.
  409.  
  410. */
  411.  
  412. struct FileInfoBlock *
  413. FileInfo(name, fib)
  414.  
  415. UBYTE                *name;
  416. struct FileInfoBlock *fib;
  417. {
  418.     BPTR handle;
  419.  
  420.     memset(fib, 0, sizeof(struct FileInfoBlock));
  421.  
  422.     if (handle = Lock(name, ACCESS_READ)) {
  423.  
  424.         if (Examine(handle, fib) == FALSE)
  425.  
  426.             fib = NULL;
  427.  
  428.         UnLock(handle);
  429.     }
  430.     else
  431.         fib = NULL;
  432.  
  433.     return(fib);
  434. }
  435.  
  436.  
  437. /* ------------------------- ResolveSymbolicPath -------------------------------
  438.  
  439.  Make symbolic path (e.g. fonts:) absolute (e.g. dh0:fonts). Writes to path.
  440.  This function fails if <path> doesn't exist.
  441.  
  442. */
  443.  
  444. BOOL
  445. ResolveSymbolicPath(path)
  446.  
  447. UBYTE *path;
  448. {
  449.     BPTR lock;
  450.  
  451.     if (lock = Lock(path, ACCESS_READ)) {
  452.  
  453.         NameFromLock(lock, path, MAX_PATHLEN);
  454.  
  455.         UnLock(lock);
  456.  
  457.         return(TRUE);
  458.     }
  459.     else
  460.         return(FALSE);
  461. }
  462.  
  463.  
  464. /* ---------------------------------- ReadFile --------------------------------
  465.  
  466.  Alloc buffer and read file to buffer. Buffer has to be FreeVec() later.
  467.  
  468. */
  469.  
  470. UBYTE *
  471. ReadFile(fileName, size)
  472.  
  473. UBYTE *fileName;
  474. ULONG *size;
  475. {
  476.     __aligned struct FileInfoBlock fib;
  477.  
  478.     UBYTE *buffer = NULL;
  479.  
  480.     if (FileInfo(fileName, &fib)) {
  481.  
  482.         if (*size = fib.fib_Size) {
  483.  
  484.             BPTR handle;
  485.  
  486.             if (handle = Open(fileName, MODE_OLDFILE)) {
  487.  
  488.                 if (buffer = AllocVec(*size, MEMF_PUBLIC)) {
  489.  
  490.                     Read(handle, buffer, *size);
  491.                 }
  492.                 else
  493.                     Message("Out of memory", TRUE, NULL);
  494.  
  495.                 Close(handle);
  496.             }
  497.             else
  498.                 Message("Couldn't open dictionary file", TRUE, NULL);
  499.         }
  500.         else
  501.            Message("Dictionary file is empty", TRUE, NULL);
  502.     }
  503.     else {
  504.  
  505.         Message("Couldn't examine dictionary file", TRUE, NULL);
  506.  
  507.         *size = 0;
  508.     }
  509.  
  510.     return(buffer);
  511. }
  512.  
  513.  
  514. /* ------------------------------- CheckThisWord -------------------------------
  515.  
  516.  Check if quickinfo exists for word at column <column> in buffer <buffer> of
  517.  size <buffersize>. Return 0-terminated info string or NULL.
  518.  
  519. */
  520.  
  521. UBYTE *
  522. CheckThisWord(context, buffer, buffersize, column)
  523.  
  524. struct PlugInContext *context;
  525. UBYTE                *buffer;
  526. UWORD                 buffersize;
  527. UWORD                 column;
  528. {
  529.     UBYTE *text;
  530.     UBYTE *last;
  531.  
  532.     text = buffer + column;
  533.     last = buffer + buffersize;
  534.  
  535.     // find a character of the last word (ignore one white space character and one boundary character if necessary)
  536.  
  537.     if (column && IsSPC[*text]) {
  538.  
  539.         --column;
  540.         --text;
  541.     }
  542.  
  543.     if (column && IsBOUNDARY[*text]) {
  544.  
  545.         --column;
  546.         --text;
  547.     }
  548.  
  549.     // any character found ?
  550.  
  551.     if (IsSPC[*text] == FALSE) {
  552.  
  553.         struct Template *template;
  554.         UBYTE           *check;
  555.         UWORD            wordlen;
  556.         UWORD            hashcode;
  557.         UWORD            ascii1;
  558.         UWORD            ascii2;
  559.  
  560.         // find beginning of word
  561.  
  562.         while (column) {
  563.  
  564.             if (IsSPC[*(text - 1)])
  565.  
  566.                 break;
  567.  
  568.             else {
  569.  
  570.                 --text;
  571.                 --column;
  572.             }
  573.         }
  574.  
  575.         // determine word length
  576.  
  577.         wordlen = 1;
  578.  
  579.         for (check = text + 1; check < last; ++check) {
  580.  
  581.             // end of word detected ?
  582.  
  583.             if (IsBOUNDARY[*check]) {
  584.  
  585.                 ++wordlen;
  586.  
  587.                 break;
  588.             }
  589.             else if (IsSPC[*check]) {
  590.  
  591.                 break;
  592.             }
  593.             else
  594.                 ++wordlen;
  595.         }
  596.  
  597.         // find sensible dictionary entry
  598.  
  599.         if (context->Ignorecase) {
  600.  
  601.             ascii1 = toupper(text[0]);
  602.  
  603.             if (wordlen == 1)
  604.                 ascii2 = 0;
  605.             else
  606.                 ascii2 = toupper(text[1]);
  607.         }
  608.         else {
  609.  
  610.             ascii1 = text[0];
  611.  
  612.             if (wordlen == 1)
  613.                 ascii2 = 0;
  614.             else
  615.                 ascii2 = text[1];
  616.         }
  617.  
  618.         hashcode = Hashcode(ascii1, ascii2, wordlen);
  619.  
  620.         // find correct entry (if any)
  621.  
  622.         if (template = context->Dictionary->Hash[hashcode]) {
  623.  
  624.             if (wordlen == 1) {
  625.  
  626.                 if (template->Len == 1)
  627.  
  628.                     return(template->Info);
  629.             }
  630.             else {
  631.  
  632.                 if (context->Ignorecase) {
  633.  
  634.                     while (template->Node.ln_Succ) {
  635.  
  636.                         if (toupper(template->Keyword[0]) <= ascii1) {
  637.  
  638.                             if (toupper(template->Keyword[1]) <= ascii2) {
  639.  
  640.                                 if (template->Len == wordlen) {
  641.  
  642.                                     WORD comparison = memicmp(template->Keyword, text, wordlen);
  643.  
  644.                                     if (comparison == 0)
  645.  
  646.                                         return(template->Info);
  647.  
  648.                                     if (comparison > 0)
  649.  
  650.                                         break;
  651.                                 }
  652.  
  653.                                 template = (struct Template *)template->Node.ln_Succ;
  654.                             }
  655.                             else
  656.                                 break;
  657.                         }
  658.                         else
  659.                             break;
  660.                     }
  661.                 }
  662.                 else {
  663.  
  664.                     while (template->Node.ln_Succ) {
  665.  
  666.                         if (template->Keyword[0] <= ascii1) {
  667.  
  668.                             if (template->Keyword[1] <= ascii2) {
  669.  
  670.                                 if (template->Len == wordlen) {
  671.  
  672.                                     WORD comparison = memcmp(template->Keyword, text, wordlen);
  673.  
  674.                                     if (comparison == 0)
  675.  
  676.                                         return(template->Info);
  677.  
  678.                                     if (comparison > 0)
  679.  
  680.                                         break;
  681.                                 }
  682.  
  683.                                 template = (struct Template *)template->Node.ln_Succ;
  684.                             }
  685.                             else
  686.                                 break;
  687.                         }
  688.                         else
  689.                             break;
  690.                     }
  691.                 }
  692.             }
  693.         }
  694.     }
  695.  
  696.     return(NULL);
  697. }
  698.  
  699.  
  700. /* --------------------------------- Hashcode ----------------------------------
  701.  
  702.  Return hashcode (0...2703) for the specified string (specified in form of 1st
  703.  and 2nd character).
  704.  
  705. */
  706.  
  707. UWORD
  708. Hashcode(ascii1, ascii2, len)
  709.  
  710. UWORD ascii1;
  711. UWORD ascii2;
  712. UWORD len;
  713. {
  714.     // convert characters to A-Z or a-z
  715.  
  716.     if (ascii1 < 'A') {
  717.  
  718.         ascii1 = 'A';
  719.     }
  720.     else if (ascii1 > 'Z') {
  721.  
  722.         if (ascii1 < 'a') {
  723.  
  724.             ascii1 = 'Z';
  725.         }
  726.         else if (ascii1 > 'z')
  727.  
  728.             ascii1 = 'z';
  729.     }
  730.  
  731.     if (ascii2 < 'A') {
  732.  
  733.         ascii2 = 'A';
  734.     }
  735.     else if (ascii2 > 'Z') {
  736.  
  737.         if (ascii2 < 'a') {
  738.  
  739.             ascii2 = 'Z';
  740.         }
  741.         else if (ascii2 > 'z')
  742.  
  743.             ascii2 = 'z';
  744.     }
  745.  
  746.     // calculate final hashcode
  747.  
  748.     if (ascii1 <= 'Z')
  749.         ascii1 = ascii1 - 'A';
  750.     else
  751.         ascii1 = ascii1 - 'a' + 26;
  752.  
  753.     if (ascii2 <= 'Z')
  754.         ascii2 = ascii2 - 'A';
  755.     else
  756.         ascii2 = ascii2 - 'a' + 26;
  757.  
  758.     return(ascii1 * 52 + ascii2);
  759. }
  760.  
  761. ///
  762. /// "gui"
  763.  
  764. /* ------------------------------ GetVScreenSize -------------------------------
  765.  
  766.  Calculate visible screen rectangle
  767.  
  768. */
  769.  
  770. void
  771. GetVScreenSize(screen, displayW, displayH)
  772.  
  773. struct Screen *screen;
  774. UWORD         *displayW;
  775. UWORD         *displayH;
  776. {
  777.     struct Rectangle clip;
  778.  
  779.     // read screen size
  780.  
  781.     if (QueryOverscan(GetVPModeID(&screen->ViewPort), &clip, OSCAN_TEXT)) {
  782.  
  783.         *displayW = clip.MaxX - clip.MinX + 1;
  784.         *displayH = clip.MaxY - clip.MinY + 1;
  785.     }
  786.     else {
  787.  
  788.         *displayW = screen->Width;
  789.         *displayH = screen->Height;
  790.     }
  791. }
  792.  
  793.  
  794. /* --------------------------------- ComputeX ----------------------------------
  795.  
  796.  Scale width <value> to match font
  797.  
  798. */
  799.  
  800. UWORD
  801. ComputeX(font, value)
  802.  
  803. struct TextFont *font;
  804. UWORD            value;
  805. {
  806.     return((font->tf_XSize * value) / 8);
  807. }
  808.  
  809.  
  810. /* --------------------------------- ComputeY ----------------------------------
  811.  
  812.  Resize height <value> to match font
  813.  
  814. */
  815.  
  816. UWORD
  817. ComputeY(font, value)
  818.  
  819. struct TextFont *font;
  820. UWORD            value;
  821. {
  822.     return((font->tf_YSize * value) / 8);
  823. }
  824.  
  825.  
  826. /* -------------------------------- OpenMessageWin -----------------------------
  827.  
  828.  Open info window. Window is closed automatically after a short delay time if
  829.  <sync> is TRUE.
  830.  
  831. */
  832.  
  833. struct Window *
  834. OpenMessageWin(text, sync)
  835.  
  836. UBYTE *text;
  837. BOOL   sync;
  838. {
  839.     struct TextFont *font;
  840.     struct Screen   *screen;
  841.     struct Window   *win;
  842.     ULONG            lock;
  843.  
  844.     win  = NULL;
  845.     font = NULL;
  846.  
  847.     // critical stuff (barely legal - don't do this at home, kids ;-)
  848.  
  849.     lock = LockIBase(0);
  850.  
  851.     // find frontmost screen
  852.  
  853.     screen = ((struct IntuitionBase *)IntuitionBase)->ActiveScreen;
  854.  
  855.     // keep system frozen
  856.  
  857.     Forbid();
  858.  
  859.     // enable rendering functions
  860.  
  861.     UnlockIBase(lock);
  862.  
  863.     if (screen) {
  864.  
  865.         struct TextAttr textAttr = { "topaz.font", 8, 0, FPF_DESIGNED };
  866.  
  867.         WORD len, width, height, fontW, fontH, x, y, displayW, displayH, xOffset, yOffset;
  868.  
  869.         GetVScreenSize(screen, &displayW, &displayH);
  870.  
  871.         xOffset = -screen->ViewPort.DxOffset;
  872.         yOffset = -screen->ViewPort.DyOffset;
  873.  
  874.         textAttr = *screen->Font;
  875.  
  876.         if (font = OpenDiskFont(&textAttr)) {
  877.  
  878.             fontW = font->tf_XSize;
  879.             fontH = font->tf_YSize;
  880.  
  881.             CloseFont(font);
  882.         }
  883.         else
  884.             fontW = fontH = textAttr.ta_YSize;
  885.  
  886.         len = strlen(text);
  887.  
  888.         if (len < 30)
  889.  
  890.             len = 30;
  891.  
  892.         width  = 20 + fontW * len;
  893.         height = 20 + fontH;
  894.  
  895.         // center window on screen
  896.  
  897.         if (displayW) {
  898.  
  899.             x = ((displayW - width )>>1) + xOffset;
  900.             y = ((displayH - height)>>1) + yOffset;
  901.         }
  902.         else
  903.             x = y = 0;
  904.  
  905.         win = OpenWindowTags(NULL,
  906.  
  907.             WA_PubScreen,     screen,
  908.             WA_Left,          x,
  909.             WA_Top,           y,
  910.             WA_InnerWidth,    width,
  911.             WA_InnerHeight,   height,
  912.             WA_Flags,         WFLG_BORDERLESS | WFLG_SMART_REFRESH | WFLG_RMBTRAP,
  913.             WA_GimmeZeroZero, TRUE,
  914.             TAG_DONE
  915.         );
  916.  
  917.         Permit();
  918.  
  919.         // render window
  920.  
  921.         if (win) {
  922.  
  923.             if (font)
  924.  
  925.                 SetFont(win->RPort, font);
  926.  
  927.             win = Message(text, sync, win);
  928.         }
  929.     }
  930.     else
  931.         Permit();
  932.  
  933.     return(win);
  934. }
  935.  
  936.  
  937. /* ---------------------------------- Message ----------------------------------
  938.  
  939.  Show message (assume that window size is sufficient)
  940.  
  941. */
  942.  
  943. struct Window *
  944. Message(text, sync, win)
  945.  
  946. struct Window *win;
  947. UBYTE         *text;
  948. BOOL           sync;
  949. {
  950.     if (win == NULL) {
  951.  
  952.         win = OpenMessageWin(text, sync);
  953.     }
  954.     else {
  955.  
  956.         struct RastPort *rast;
  957.         struct DrawInfo *drawInfo;
  958.  
  959.         rast = win->RPort;
  960.  
  961.         // render background and frame
  962.  
  963.         if (drawInfo = GetScreenDrawInfo(win->WScreen)) {
  964.  
  965.             SetAPen(rast, drawInfo->dri_Pens[SHINEPEN]);
  966.  
  967.             RectFill(rast, 2, 2, win->Width - 3, win->Height - 3);
  968.  
  969.             SetAPen(rast, drawInfo->dri_Pens[SHADOWPEN]);
  970.             SetBPen(rast, drawInfo->dri_Pens[SHINEPEN ]);
  971.  
  972.             Move(rast, 0, 0);
  973.             Draw(rast, win->Width - 1, 0);
  974.             Draw(rast, win->Width - 1, win->Height - 1);
  975.             Draw(rast, 0,              win->Height - 1);
  976.             Draw(rast, 0,              0);
  977.  
  978.             Move(rast, 1, 1);
  979.             Draw(rast, win->Width - 2, 1);
  980.             Draw(rast, win->Width - 2, win->Height - 2);
  981.             Draw(rast, 1,              win->Height - 2);
  982.             Draw(rast, 1,              1);
  983.  
  984.             FreeScreenDrawInfo(win->WScreen, drawInfo);
  985.         }
  986.  
  987.         // render new text
  988.  
  989.         Move(rast, 10, 10 + rast->TxBaseline);
  990.  
  991.         Text(rast, text, strlen(text));
  992.  
  993.         if (sync) {
  994.  
  995.             Delay(150);
  996.  
  997.             CloseWindow(win);
  998.  
  999.             win = NULL;
  1000.         }
  1001.     }
  1002.  
  1003.     return(win);
  1004. }
  1005.  
  1006. ///
  1007. /// "container"
  1008.  
  1009.  
  1010. /* ----------------------------- DispatchContainer -----------------------------
  1011.  
  1012.  Handle container-related event
  1013.  
  1014. */
  1015.  
  1016. ULONG
  1017. DispatchContainer(context, apiMsg)
  1018.  
  1019. struct PlugInContext *context;
  1020. struct APIMessage    *apiMsg;
  1021. {
  1022.     ULONG retval = API_ERROR_OK;
  1023.  
  1024.     switch (apiMsg->api_Action) {
  1025.  
  1026.         case API_ACTION_ATTACH:
  1027.  
  1028.             context->Attached = TRUE;
  1029.  
  1030.             break;
  1031.  
  1032.         case API_ACTION_DETACH:
  1033.  
  1034.             context->Attached = FALSE;
  1035.  
  1036.             break;
  1037.  
  1038.         case API_ACTION_PAINT:
  1039.         case API_ACTION_REFRESH:
  1040.  
  1041.             ShowInfo(apiMsg, context, NULL);
  1042.  
  1043.             break;
  1044.  
  1045.         default:
  1046.  
  1047.             retval = API_ERROR_UNKNOWN;
  1048.     }
  1049.  
  1050.     return(retval);
  1051. }
  1052.  
  1053.  
  1054. /* ------------------------------ ClearContainer -------------------------------
  1055.  
  1056.  Clear container
  1057.  
  1058. */
  1059.  
  1060. void
  1061. ClearContainer(apiMsg, context)
  1062.  
  1063. struct APIMessage    *apiMsg;
  1064. struct PlugInContext *context;
  1065. {
  1066.     if (context->Attached && context->Text) {
  1067.  
  1068.         struct APIInstance  *instance;
  1069.         struct APIContainer *container;
  1070.  
  1071.         instance = apiMsg->api_Instance;
  1072.  
  1073.         if (container = instance->api_Container) {
  1074.  
  1075.             // container not fully obscured ?
  1076.  
  1077.             if (container->api_Clipping) {
  1078.  
  1079.                 struct RastPort *rast = container->api_RPort;
  1080.  
  1081.                 SetAPen(rast, container->api_DrawInfo->dri_Pens[BACKGROUNDPEN]);
  1082.  
  1083.                 // clear container
  1084.  
  1085.                 RectFill(rast, container->api_Clipping->MinX, container->api_Clipping->MinY, container->api_Clipping->MaxX, container->api_Clipping->MaxY);
  1086.             }
  1087.         }
  1088.  
  1089.         // remember: display is clear
  1090.  
  1091.         context->Text = NULL;
  1092.     }
  1093. }
  1094.  
  1095.  
  1096. /* --------------------------------- ShowInfo ----------------------------------
  1097.  
  1098.  Show <text> (show last text if <text> is NULL) in our container (
  1099.  
  1100. */
  1101.  
  1102. void
  1103. ShowInfo(apiMsg, context, text)
  1104.  
  1105. struct APIMessage    *apiMsg;
  1106. struct PlugInContext *context;
  1107. UBYTE                *text;
  1108. {
  1109.     if (context->Status) {
  1110.  
  1111.         apiMsg->api_Status = text;
  1112.  
  1113.     }
  1114.     else if (context->Attached) {
  1115.  
  1116.         // (forced) redisplay of last text ?
  1117.  
  1118.         if (text == NULL) {
  1119.  
  1120.             text = context->Text;
  1121.         }
  1122.         else if (context->Text) {
  1123.  
  1124.             // same text ?
  1125.  
  1126.             if (strcmp(context->Text, text) == 0)
  1127.  
  1128.                 text = NULL;
  1129.         }
  1130.  
  1131.         if (text) {
  1132.  
  1133.             struct APIInstance  *instance;
  1134.             struct APIContainer *container;
  1135.  
  1136.             instance = apiMsg->api_Instance;
  1137.  
  1138.             ClearContainer(apiMsg, context);
  1139.  
  1140.             if (container = instance->api_Container) {
  1141.  
  1142.                 // container not fully obscured ?
  1143.  
  1144.                 if (container->api_Clipping) {
  1145.  
  1146.                     struct TextFont *font;
  1147.  
  1148.                     if (font = OpenDiskFont(container->api_TextAttr)) {
  1149.  
  1150.                         UWORD x0, y0, x1, y1;
  1151.                         UWORD containerW, textW;
  1152.                         UWORD containerH, textH;
  1153.  
  1154.                         context->Text = text;
  1155.  
  1156.                         // determine space available for text
  1157.  
  1158.                         x0 = container->api_Clipping->MinX;
  1159.                         y0 = container->api_Clipping->MinY;
  1160.                         x1 = container->api_Clipping->MaxX;
  1161.                         y1 = container->api_Clipping->MaxY;
  1162.  
  1163.                         containerW = x1 - x0 + 1;
  1164.                         containerH = y1 - y0 + 1;
  1165.  
  1166.                         if ((containerW >= font->tf_XSize) && (containerH >= font->tf_YSize)) {
  1167.  
  1168.                             struct RastPort *rast = container->api_RPort;
  1169.  
  1170.                             UWORD minW = font->tf_XSize + font->tf_XSize;
  1171.                             UWORD minH = font->tf_YSize + font->tf_YSize;
  1172.  
  1173.                             textW = containerW;
  1174.                             textH = containerH;
  1175.  
  1176.                             // maintain distance to the container borders (if possible)
  1177.  
  1178.                             if (textW > minW) {
  1179.  
  1180.                                 if (textW >= (minW + 8))
  1181.                                     textW -= 8;
  1182.                                 else
  1183.                                     textW -= (textW - minW);
  1184.                             }
  1185.                             else
  1186.                                 textW = (textW / font->tf_XSize) * font->tf_XSize;
  1187.  
  1188.                             if (textH > minH) {
  1189.  
  1190.                                 if (textH >= (minH + 8))
  1191.                                     textH -= 8;
  1192.                                 else
  1193.                                     textH -= (textH - minH);
  1194.                             }
  1195.                             else
  1196.                                 textH = (textH / font->tf_YSize) * font->tf_YSize;
  1197.  
  1198.                             x0 += (containerW - textW) / 2;
  1199.                             y0 += (containerH - textH) / 2 + font->tf_Baseline;
  1200.  
  1201.                             SetFont(rast, font);
  1202.  
  1203.                             SetAPen(rast, container->api_DrawInfo->dri_Pens[SHADOWPEN]);
  1204.  
  1205.                             // split text into lines
  1206.  
  1207.                             while (*text && (textH >= font->tf_YSize)) {
  1208.  
  1209.                                 struct TextExtent textExtent;
  1210.                                 UWORD             fit;
  1211.  
  1212.                                 if (fit = TextFit(rast, text, strlen(text), &textExtent, NULL, 1, textW, textH)) {
  1213.  
  1214.                                     Move(rast, x0, y0);
  1215.  
  1216.                                     Text(rast, text, fit);
  1217.  
  1218.                                     y0    += font->tf_YSize;
  1219.                                     textH -= font->tf_YSize;
  1220.                                     text  += fit;
  1221.                                 }
  1222.                                 else
  1223.                                     break;
  1224.                             }
  1225.                         }
  1226.  
  1227.                         CloseFont(font);
  1228.                     }
  1229.                 }
  1230.             }
  1231.         }
  1232.     }
  1233. }
  1234.  
  1235. ///
  1236. /// "misc"
  1237.  
  1238. /* ---------------------------------- memicmp ----------------------------------
  1239.  
  1240.  case-insensitive string compare (return 0 if identical)
  1241.  
  1242. */
  1243.  
  1244. WORD
  1245. memicmp(stringA, stringB, len)
  1246.  
  1247. UBYTE *stringA;
  1248. UBYTE *stringB;
  1249. UWORD  len;
  1250. {
  1251.     while (len--) {
  1252.  
  1253.         UWORD asciiA, asciiB;
  1254.  
  1255.         asciiA = toupper(*stringA++);
  1256.         asciiB = toupper(*stringB++);
  1257.  
  1258.         if (asciiA > asciiB)
  1259.  
  1260.             return(+1);
  1261.  
  1262.         if (asciiA < asciiB)
  1263.  
  1264.             return(-1);
  1265.     }
  1266.  
  1267.     return(0);
  1268. }
  1269.  
  1270. ////
  1271.